package sel.me.popular.library.learning.serialize;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.HessianInput;
import com.caucho.hessian.io.HessianOutput;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.thoughtworks.xstream.XStream;

public class Test {

    final static int testCount = 1000000;


    public static void main(String[] args) {

        // 序列化
        testTemplate(new TestCallBack() {

            public String getName() {
                return "Serializable Test";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    ObjectOutputStream output = new ObjectOutputStream(bout);
                    output.writeObject(source);
                    return bout.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
                    ObjectInputStream input = new ObjectInputStream(bin);
                    return input.readObject();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, getPerson(), testCount);


        // Xstream测试
        final XStream xstream = new XStream();
        testTemplate(new TestCallBack() {

            public String getName() {
                return "Xstream test";
            }

            @Override
            public byte[] writeObject(Object source) {
                return xstream.toXML(source).getBytes();
            }

            @Override
            public Object readObject(byte[] bytes) {
                return xstream.fromXML(new ByteArrayInputStream(bytes));
            }
        }, getPerson(), testCount);

        // json测试
        testTemplate(new TestCallBack() {

            public String getName() {
                return "fastJson Test";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                //String tmp =JSON.toJSONString(source);
                //System.out.println(":"+tmp);
                    return JSON.toJSONString(source).getBytes();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    //String tmp =new String(bytes);
                    //System.out.println("parse before:"+tmp);
                    JSONObject object = JSON.parseObject(new String(bytes));

                    return object;
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, getPerson(), testCount);


        // json测试
        final ObjectMapper objectMapper = new ObjectMapper();

        // JSON configuration for compatibility
        objectMapper.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
        objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);

        testTemplate(new TestCallBack() {

            public String getName() {
                return "Jackson Test";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    return objectMapper.writeValueAsBytes(source);
                } catch (JsonGenerationException e) {
                    e.printStackTrace();
                } catch (JsonMappingException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    return objectMapper.readValue(bytes, 0, bytes.length, Person.class);
                } catch (JsonParseException e) {
                    e.printStackTrace();
                } catch (JsonMappingException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, getPerson(), testCount);

        //hession
        testTemplate(new TestCallBack() {
            public String getName() {
                return "hessian 2 with no deflat";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    Hessian2Output out = new Hessian2Output(bos);
                    // out.startMessage();
                    out.writeObject(source);
                    // out.completeMessage();
                    out.flush();
                    return bos.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
                    Hessian2Input in = new Hessian2Input(bin);
                    // in.startMessage();
                    Object obj = in.readObject();
                    // in.completeMessage();
                    return obj;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, getPerson(), testCount);

        // hessian 1 with no deflat
        testTemplate(new TestCallBack() {

            public String getName() {
                return "hessian 1 with no deflat";
            }

            @Override
            public byte[] writeObject(Object source) {
                try {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    HessianOutput out = new HessianOutput(bos);
                    out.writeObject(source);
                    out.flush();
                    return bos.toByteArray();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public Object readObject(byte[] bytes) {
                try {
                    ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
                    HessianInput in = new HessianInput(bin);
                    Object obj = in.readObject();
                    in.close();
                    return obj;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        }, getPerson(), testCount);
    }

    private static void testTemplate(TestCallBack callback, Object source, int count) {
        int warmup = 10;
        // 先进行预热，加载一些类，避免影响测试
        for (int i = 0; i < warmup; i++) {
            byte[] bytes = callback.writeObject(source);
            callback.readObject(bytes);
        }
        restoreJvm(); // 进行GC回收
        // 进行测试
        long start = System.nanoTime();
        long size = 0l;
        for (int i = 0; i < count; i++) {
            byte[] bytes = callback.writeObject(source);
            // System.out.println("byte:"+bytes.toString());
            size = size + bytes.length;
            callback.readObject(bytes);
            // System.out.println(callback.readObject(bytes));
            bytes = null;
        }
        long nscost = (System.nanoTime() - start);

        System.out.println(callback.getName() + " total cost=" + nscost + "ns , each cost="
                + (nscost / count) + "ns , and byte sizes = " + size / count);
        restoreJvm();// 进行GC回收

    }

    private static void restoreJvm() {
        int maxRestoreJvmLoops = 10;
        long memUsedPrev = memoryUsed();
        for (int i = 0; i < maxRestoreJvmLoops; i++) {
            System.runFinalization();
            System.gc();

            long memUsedNow = memoryUsed();
            // break early if have no more finalization and get constant mem used
            if ((ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount() == 0)
                    && (memUsedNow >= memUsedPrev)) {
                break;
            } else {
                memUsedPrev = memUsedNow;
            }
        }
    }

    private static long memoryUsed() {
        Runtime rt = Runtime.getRuntime();
        return rt.totalMemory() - rt.freeMemory();
    }

    public byte[] writeObject(Object source) {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            Hessian2Output out = new Hessian2Output(bos);
            // out.startMessage();
            out.writeObject(source);
            // out.completeMessage();
            out.flush();
            return bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public Object readObject(byte[] bytes) {
        try {
            ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
            Hessian2Input in = new Hessian2Input(bin);
            // in.startMessage();
            Object obj = in.readObject();
            // in.completeMessage();
            return obj;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static Person getPerson() {
        Person person = new Person();
        List l = new ArrayList();
        l.add("Beijing");
        l.add("TaiWan");
        person.setAddress(l);
        person.setBrithday(new Date());
        person.setGender(false);
        person.setHeight(168.5D);
        person.setId(300);
        person.setName("Jack");
        person.setPhone(188888888);
        person.setWeight(55.2F);
        return person;
    }
}





